package com.demo7;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.ModuleListener;
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.arm.backend.EventMemHook;
import com.github.unidbg.arm.backend.UnHook;
import com.github.unidbg.arm.context.RegisterContext;
import com.github.unidbg.debugger.BreakPointCallback;
import com.github.unidbg.pointer.UnidbgPointer;
import unicorn.ArmConst;
import unicorn.UnicornConst;


import java.util.ArrayList;

public class hookFuncs implements ModuleListener {
    protected final Emulator<?> emulator;
    protected String functionsPath;
    protected String soName;
    protected RegisterContext registerContext;
    protected ArrayList<String> callFunctions = new ArrayList<String>();;
    protected Module module;

    public hookFuncs(Emulator<?> emulator, String soName, String pathName){
        this.emulator = emulator;
        this.functionsPath = pathName;
        this.soName = soName;
        this.registerContext = emulator.getContext();
    }

    @Override
    public void onLoaded(Emulator<?> emulator, Module module) {
        // 在目标函数中Hook
        if(module.name.equals(this.soName)) {
            this.module = module;
            hookNormal();
            hookPopen();
            patch();
        }
    }

    private void hookPopen() {
        emulator.attach().addBreakPoint(module.findSymbolByName("popen").getAddress(), new BreakPointCallback() {
            UnidbgPointer idptr = UnidbgPointer.pointer(emulator, module.findSymbolByName("fopen").call(emulator, "id", "r"));
            @Override
            public boolean onHit(Emulator<?> emulator, long address) {
                System.out.println("SO call popen");
                RegisterContext registerContext = emulator.getContext();
                String cmd = registerContext.getPointerArg(0).getString(0);
                System.out.println("popen arg:"+cmd);
                emulator.getBackend().reg_write(ArmConst.UC_ARM_REG_PC, registerContext.getLRPointer().peer);
                if(cmd.contains("magisk")){
                    emulator.getBackend().reg_write(ArmConst.UC_ARM_REG_R0, 0);
                    return true;
                }
                if(cmd.equals("id")){
                    emulator.getBackend().reg_write(ArmConst.UC_ARM_REG_R0, idptr.peer);
                    return true;
                }
                if(cmd.contains("monkey")){
                    emulator.getBackend().reg_write(ArmConst.UC_ARM_REG_R0, 0);
                    return true;
                }
                return false;
            }
        });
    }

    private void hookNormal() {
        // 这里Unidbg还不够完善，建议有志之士直接修复，而不是patch阻止使用，我这里偷懒了嗷
        long bindAddress = module.findSymbolByName("bind").getAddress();
        emulator.attach().addBreakPoint(bindAddress, new BreakPointCallback() {
            @Override
            public boolean onHit(Emulator<?> emulator, long address) {
                emulator.getBackend().reg_write(ArmConst.UC_ARM_REG_PC, registerContext.getLRPointer().peer);
                emulator.getBackend().reg_write(ArmConst.UC_ARM_REG_R0, -1);
                return true;
            }
        });
    }

    // methodid非结构体带来的问题
    public void patch(){
        emulator.getBackend().hook_add_new(new EventMemHook() {
            @Override
            public boolean hook(Backend backend, long address, int size, long value, Object user, UnmappedType unmappedType) {
                if(address > 0x1000){
                    emulator.getBackend().mem_map(address / 0x1000 * 0x1000,0x1000, UnicornConst.UC_PROT_ALL);
                    emulator.getBackend().mem_write(address, new byte[4]);
                }
                return true;
            }

            @Override
            public void onAttach(UnHook unHook) {
            }
            @Override
            public void detach() {
                throw new UnsupportedOperationException();
            }
        }, UnicornConst.UC_HOOK_MEM_READ_UNMAPPED | UnicornConst.UC_HOOK_MEM_WRITE_UNMAPPED | UnicornConst.UC_HOOK_MEM_FETCH_UNMAPPED, null);
    }
}
